package com.example.oauth;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
//import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.util.Arrays;
import java.util.HashMap;

@Configuration
public class BeanConfig {

    @Autowired
    RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore tokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler() {
        return new MyAuthenticationSuccessHandler();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {

        };
        accessTokenConverter.setSigningKey("123");
        return accessTokenConverter;
    }

    @Configuration
    public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {



        @Autowired
        PasswordEncoder passwordEncoder;

//        @Autowired
//        LogoutSuccessHandler logoutSuccessHandler;

        @Autowired
        MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter;

        @Autowired
        MyLoginAuthenticationProvider authenticationProvider;

        @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {

            myUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
            myUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(
                    new MyAuthenticationSuccessHandler()
            );

            MyLoginAuthenticationProvider provider = new MyLoginAuthenticationProvider();

            http = http.authenticationProvider(provider).addFilterBefore(
                    myUsernamePasswordAuthenticationFilter,
                    UsernamePasswordAuthenticationFilter.class);
            http
                    .authorizeRequests()
                    .antMatchers("/mylogin*", "/authentication*").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/mylogin")
                    .and()
                    .logout()
                    .and().csrf().disable();

        }

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.authenticationProvider(authenticationProvider);
            auth.inMemoryAuthentication()
                    .withUser("user1").password(passwordEncoder.encode("123")).roles("admin");
        }
    }

    @EnableAuthorizationServer
    @Configuration
    public static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        TokenStore tokenStore;

        @Autowired
        AuthenticationManager authenticationManager;

        @Autowired
        PasswordEncoder passwordEncoder;

        @Autowired
        JwtAccessTokenConverter accessTokenConverter;

        @Autowired
        MyTokenEnhancer tokenEnhancer;

        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.allowFormAuthenticationForClients();
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

            clients.inMemory()
                    .withClient("client1")
                    .redirectUris("http://localhost:8081")
                    .authorizedGrantTypes("refresh_token", "implicit", "password", "client_credentials", "authorization_code")
                    .scopes("all")
                    .additionalInformation(new HashMap<String, String>() {
                        {
                            put("loginPage", "http://localhost:8081/login.html");
                            put("mainPage", "htt://localhost:8081/");
                        }
                    })
                    .autoApprove(true)
                    .secret(passwordEncoder.encode("123"))
                    .and()
                    .withClient("client2")
                    .authorizedGrantTypes("refresh_token", "implicit", "password", "client_credentials", "authorization_code")
                    .scopes("all")
                    .additionalInformation(new HashMap<String, String>() {
                        {
                            put("loginPage", "http://localhost:8082/login.html");
                            put("mainPage", "htt://localhost:8082/");
                        }
                    })
                    .autoApprove(true)
                    .redirectUris("http://localhost:8082")
                    .secret(passwordEncoder.encode("123"));
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            super.configure(endpoints);

            TokenGranter tokenGranter = getTokenGranter(endpoints);

            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            tokenEnhancerChain.setTokenEnhancers(Arrays.asList(
                    tokenEnhancer, accessTokenConverter
            ));

            endpoints
//                    .tokenGranter(tokenGranter)
//                    .tokenStore(tokenStore)
                    .tokenEnhancer(tokenEnhancerChain)
//                    .tokenStore(new JwtTokenStore(accessTokenConverter))
                    .accessTokenConverter(accessTokenConverter)
                    .authenticationManager(authenticationManager);
        }

        private TokenGranter getTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
            MyTokenGranter tokenGranter = new MyTokenGranter(endpoints.getTokenServices(),
                    endpoints.getClientDetailsService(),
                    endpoints.getOAuth2RequestFactory(), "token");

            return tokenGranter;
        }
    }


}
